/*-------------------------------------------------------------------------
 *
 * deparse_database_stmts.c
 *
 *	  All routines to deparse database statements.
 *
 * Copyright (c), Citus Data, Inc.
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "catalog/namespace.h"
#include "commands/defrem.h"
#include "lib/stringinfo.h"
#include "nodes/parsenodes.h"
#include "utils/builtins.h"

#include "pg_version_compat.h"

#include "distributed/citus_ruleutils.h"
#include "distributed/deparser.h"
#include "distributed/log_utils.h"

static void AppendAlterDatabaseOwnerStmt(StringInfo buf, AlterOwnerStmt* stmt);
static void AppendAlterDatabaseStmt(StringInfo buf, AlterDatabaseStmt* stmt);

char* DeparseAlterDatabaseOwnerStmt(Node* node)
{
    AlterOwnerStmt* stmt = castNode(AlterOwnerStmt, node);
    StringInfoData str = {0};
    initStringInfo(&str);

    Assert(stmt->objectType == OBJECT_DATABASE);

    AppendAlterDatabaseOwnerStmt(&str, stmt);

    return str.data;
}

static void AppendAlterDatabaseOwnerStmt(StringInfo buf, AlterOwnerStmt* stmt)
{
    Assert(stmt->objectType == OBJECT_DATABASE);
    appendStringInfo(buf, "ALTER DATABASE %s OWNER TO %s;",
                     quote_identifier(strVal(linitial(stmt->object))),
                     RoleSpecString(stmt->newowner, true));
}

static void AppendGrantDatabases(StringInfo buf, GrantStmt* stmt)
{
    ListCell* cell = NULL;
    appendStringInfo(buf, " ON DATABASE ");

    foreach (cell, stmt->objects) {
        char* database = strVal(lfirst(cell));
        appendStringInfoString(buf, quote_identifier(database));
        if (cell != list_tail(stmt->objects)) {
            appendStringInfo(buf, ", ");
        }
    }
}

static void AppendGrantOnDatabaseStmt(StringInfo buf, GrantStmt* stmt)
{
    Assert(stmt->objtype == ACL_OBJECT_DATABASE);

    AppendGrantSharedPrefix(buf, stmt);

    AppendGrantDatabases(buf, stmt);

    AppendGrantSharedSuffix(buf, stmt);
}

static void AppendDefElemConnLimit(StringInfo buf, DefElem* def)
{
    appendStringInfo(buf, " CONNECTION LIMIT %ld", (long int)defGetNumeric(def));
}

static void AppendAlterDatabaseStmt(StringInfo buf, AlterDatabaseStmt* stmt)
{
    appendStringInfo(buf, "ALTER DATABASE %s ", quote_identifier(stmt->dbname));

    if (stmt->options) {
        ListCell* cell = NULL;
        appendStringInfo(buf, "WITH ");
        foreach (cell, stmt->options) {
            DefElem* def = castNode(DefElem, lfirst(cell));
            if (strcmp(def->defname, "is_template") == 0) {
                appendStringInfo(buf, "IS_TEMPLATE %s",
                                 quote_literal_cstr(strVal(def->arg)));
            } else if (strcmp(def->defname, "connection_limit") == 0) {
                AppendDefElemConnLimit(buf, def);
            } else if (strcmp(def->defname, "allow_connections") == 0) {
#ifdef DISABLE_OG_COMMENTS
                ereport(ERROR, errmsg("ALLOW_CONNECTIONS is not supported"));
#endif
            } else {
#ifdef DISABLE_OG_COMMENTS
                ereport(ERROR,
                        errmsg("unrecognized ALTER DATABASE option: %s", def->defname));
#endif
            }
        }
    }

    appendStringInfo(buf, ";");
}

char* DeparseGrantOnDatabaseStmt(Node* node)
{
    GrantStmt* stmt = castNode(GrantStmt, node);
    Assert(stmt->objtype == ACL_OBJECT_DATABASE);

    StringInfoData str = {0};
    initStringInfo(&str);

    AppendGrantOnDatabaseStmt(&str, stmt);

    return str.data;
}

char* DeparseAlterDatabaseStmt(Node* node)
{
    AlterDatabaseStmt* stmt = castNode(AlterDatabaseStmt, node);

    StringInfoData str = {0};
    initStringInfo(&str);

    AppendAlterDatabaseStmt(&str, stmt);

    return str.data;
}

char* DeparseAlterDatabaseRefreshCollStmt(Node* node)
{
#ifdef DISABLE_OG_COMMENTS
    AlterDatabaseRefreshCollStmt* stmt = (AlterDatabaseRefreshCollStmt*)node;

    StringInfoData str;
    initStringInfo(&str);

    appendStringInfo(&str, "ALTER DATABASE %s REFRESH COLLATION VERSION;",
                     quote_identifier(stmt->dbname));

    return str.data;
#endif
    return NULL;
}
